# -*- coding: utf-8 -*-
# FileName:     raid_simulator_gui.py
# time:         22/12/4 004 下午 6:40
# Author:       Zhou Hang
# Description:  RAID 模拟器 GUI
import sys
from pathlib import Path

from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtGui import QTextCursor, QDesktopServices
from PyQt5.QtWidgets import QMainWindow, QMessageBox, QTableWidgetItem

from RaidSim.src.core.raid import core
from common.zerror import MyCoreError
from RaidSim.src.ui.raid_simulator_ui import Ui_Raid_Simulator_mw


class Raid_Simulator_gui(QMainWindow, Ui_Raid_Simulator_mw):
    def __init__(self):
        super(Raid_Simulator_gui, self).__init__()
        self.core = None
        self.setupUi(self)
        self.gui_init()
        self.clear_btn.clicked.connect(self.click_clear_btn)
        self.start_btn.clicked.connect(self.click_start_btn)
        self.writeFrac_slider.valueChanged.connect(self.slider_valueChanged)
        self.level_cb.currentIndexChanged.connect(self.level_changed)
        self.numDisks_sb.valueChanged.connect(self.level_changed)
        self.raid5_checkBox.stateChanged.connect(self.level_changed)
        self.exit_btn.triggered.connect(self.exit_window)
        self.help_btn.triggered.connect(self.help_window)
        self.level_changed()

    def gui_init(self):
        """做一些界面初始化"""
        self.level_cb.addItems(["0 条带化", "1 镜像化", "4 单盘奇偶校验", "5 多盘奇偶校验"])
        self.chunkSize_cb.addItems(["K", 'M'])
        self.reqSize_cb.addItems(["K", "M"])
        self.workload_cb.addItems(["随机", "顺序"])

    def exit_window(self):
        self.close()

    def help_window(self):
        """显示帮助文档，打开gitee网站README"""
        QDesktopServices.openUrl(QUrl("https://gitee.com/zhouzhoukang/ostools"))

    def slider_valueChanged(self):
        self.writeFrac_lbl.setText(f'写占比 {self.writeFrac_slider.value()}%')

    def level_changed(self):
        self.raid_table.clear()
        default_max_offset = 20
        numDisks = self.numDisks_sb.value()
        if numDisks > 10:
            QMessageBox.information(self, '警告', '你还想要多少磁盘？')
            self.numDisks_sb.setValue(1)
            return
        level = int(self.level_cb.currentText()[0])
        if level == 1:
            if numDisks % 2 != 0:
                numDisks += 1
                self.numDisks_sb.setValue(numDisks)

        self.raid_table.setColumnCount(numDisks)
        self.raid_table.setRowCount(default_max_offset)
        self.raid_table.setHorizontalHeaderLabels([f'磁盘{i}' for i in range(numDisks)])
        self.raid_table.setVerticalHeaderLabels([f'{i}' for i in range(default_max_offset)])

        if level == 0:
            for addr in range(default_max_offset * numDisks):
                item = QTableWidgetItem(f'{addr}')
                item.setTextAlignment(int(Qt.AlignHCenter) | int(Qt.AlignVCenter))
                self.raid_table.setItem(addr // numDisks, addr % numDisks, item)
        elif level == 1:
            # 镜像盘，必须要偶数块磁盘才能镜像
            group_num = numDisks // 2
            row_table = range(0, numDisks, 2)
            for addr in range(default_max_offset * group_num):
                item = QTableWidgetItem(f'{addr}')
                item.setTextAlignment(int(Qt.AlignHCenter) | int(Qt.AlignVCenter))
                self.raid_table.setItem(addr // group_num, row_table[addr % group_num], item)
                item = QTableWidgetItem(f'{addr}')
                item.setTextAlignment(int(Qt.AlignHCenter) | int(Qt.AlignVCenter))
                self.raid_table.setItem(addr // group_num, row_table[addr % group_num] + 1, item)
        elif level == 4:
            # 以最后一块作为奇偶校验盘
            for addr in range(default_max_offset * (numDisks - 1)):
                item = QTableWidgetItem(f'{addr}')
                item.setTextAlignment(int(Qt.AlignHCenter) | int(Qt.AlignVCenter))
                self.raid_table.setItem(addr // (numDisks - 1), addr % (numDisks - 1), item)
                item = QTableWidgetItem('🔍')
                item.setTextAlignment(int(Qt.AlignHCenter) | int(Qt.AlignVCenter))
                self.raid_table.setItem(addr // (numDisks - 1), numDisks - 1, item)
        elif level == 5:
            # 这里的排布就比较奇特了, 应该分为LA和LS
            # 这里写的非常拉跨，但是能用，可维护性为负，但是好像也不需要维护
            raid5_layout = 'LS' if self.raid5_checkBox.checkState() else 'LA'
            col_id, row_id = 0, 0
            addr = 0
            checksum_timer, newline_timer = 0, 0
            checksum_trigger = (numDisks - 1)
            checksum_shutdown = False
            while row_id < default_max_offset:
                old_col_id = col_id
                if checksum_timer == checksum_trigger and not checksum_shutdown:
                    checksum_timer = 0
                    item = QTableWidgetItem('🔍')
                    item.setTextAlignment(int(Qt.AlignHCenter) | int(Qt.AlignVCenter))
                    if raid5_layout == 'LA':
                        checksum_shutdown = True
                        checksum_trigger = (checksum_trigger - 1) % numDisks
                        col_id = (col_id + 1) % numDisks
                else:
                    if not checksum_shutdown:
                        checksum_timer += 1
                    item = QTableWidgetItem(f'{addr}')
                    item.setTextAlignment(int(Qt.AlignHCenter) | int(Qt.AlignVCenter))
                    addr += 1
                    col_id = (col_id + 1) % numDisks
                self.raid_table.setItem(row_id, old_col_id, item)
                newline_timer += 1
                if newline_timer == numDisks:
                    if raid5_layout == 'LA':
                        checksum_shutdown = False
                        checksum_timer = 0
                    newline_timer = 0
                    row_id += 1

    def click_clear_btn(self):
        """将参数返回默认值"""
        self.level_cb.setCurrentIndex(0)
        self.chunkSize_cb.setCurrentIndex(0)
        self.reqSize_cb.setCurrentIndex(0)
        self.numRequests_sb.setValue(10)
        self.numDisks_sb.setValue(4)
        self.chunkSize_sb.setValue(4)
        self.workload_cb.setCurrentIndex(0)
        self.writeFrac_slider.setValue(0)
        self.reqSize_sb.setValue(4)
        self.timing_checkBox.setChecked(False)
        self.seed_sb.setValue(0)
        self.randRange_sb.setValue(20)
        self.raid5_checkBox.setChecked(True)

    def click_start_btn(self):
        self.quesion_text.clear()
        seed = self.seed_sb.value()
        numDisks = self.numDisks_sb.value()
        chunkSize = f'{self.chunkSize_sb.value()}{self.chunkSize_cb.currentText()}'
        numRequests = self.numRequests_sb.value()
        reqSize = f'{self.reqSize_sb.value()}{self.reqSize_cb.currentText()}'
        workload = 'rand' if self.workload_cb.currentText() == '随机' else 'seq'
        writeFrac = self.writeFrac_slider.value()
        randRange = self.randRange_sb.value()
        level = int(self.level_cb.currentText()[0])
        raid5 = 'LS' if self.raid5_checkBox.checkState() else 'LA'
        timing = True if self.timing_checkBox.checkState() else False

        save_stdout = sys.stdout
        sys.stdout = open("../output/res.txt", "w", encoding='utf-8')
        try:
            self.core = core(seed, numDisks, chunkSize, numRequests, reqSize, workload, writeFrac,
                             randRange, level, raid5, timing)
        except MyCoreError as e:
            QMessageBox.warning(self, '警告', str(e))
            return
        finally:
            sys.stdout = save_stdout

        result = Path("../output/res.txt")
        self.quesion_text.append(result.read_text(encoding='utf-8'))
        self.quesion_text.moveCursor(QTextCursor.Start)


def main():
    pass


if __name__ == "__main__":
    main()
